﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Markup;

namespace Geometry
{
    public sealed class OrthogonalProjectionPoint : GObjectBase, IGPoint
    {
        public OrthogonalProjectionPoint()
        {
            UpdateBoundingBox();
        }

        public OrthogonalProjectionPoint(IGPoint _point, IGLine _line)
        {
            this.Point = _point;
            this.Line = _line;
            UpdateBoundingBox();
        }

        protected override void ReleaseDependencies()
        {
            this.Point = null;
            this.Line = null;
        }

        protected override void UpdateBoundingBox()
        {
            base.BoundingBox = new Rect(Position, Position);
        }

        private IGPoint m_Point;
        public IGPoint Point
        {
            get
            {
                return m_Point;
            }
            set
            {
                if (m_Point == value) return;

                if (m_Point != null) m_Point.PropertyChanged -= new PropertyChangedEventHandler(_related_PropertyChanged);
                m_Point = value;
                if (m_Point != null) m_Point.PropertyChanged += new PropertyChangedEventHandler(_related_PropertyChanged);

                RaisePropertyChanged("Point");
                UpdatePosition();
            }
        }

        private IGLine m_Line;
        public IGLine Line
        {
            get
            {
                return m_Line;
            }
            set
            {
                if (m_Line == value) return;

                if (m_Line != null) m_Line.PropertyChanged -= new PropertyChangedEventHandler(_related_PropertyChanged);
                m_Line = value;
                if (m_Line != null) m_Line.PropertyChanged += new PropertyChangedEventHandler(_related_PropertyChanged);

                RaisePropertyChanged("Line");
                UpdatePosition();
            }
        }

        void _related_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "Position" && e.PropertyName != "Slope") return;

            UpdatePosition();
        }

        private void UpdatePosition()
        {
            if (m_Point.IsNull() || m_Line.IsNull()) return;
            var p1 = m_Line.Point1;
            var p2 = m_Line.Point2;
            var p3 = m_Point.Position;
            var d = (p1 - p2);
            if (d.LengthSquared == 0) this.Position = new Point(p1.X, p1.Y);
            var u = (p3.X - p1.X) * (p2.X - p1.X) + (p3.Y - p1.Y) * (p2.Y - p1.Y);
            u /= d.LengthSquared;
            var x = (p2.X - p1.X) * u + p1.X;
            var y = (p2.Y - p1.Y) * u + p1.Y;

            this.Position = new Point(x, y);
        }

        #region IGPoint Members
        private Point m_Position;

        public double PosX
        {
            get { return m_Position.X; }
            private set
            {
                if (value.Equals(m_Position.X)) return;
                m_Position.X = value;
                UpdateBoundingBox();
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("Position");
            }
        }

        public double PosY
        {
            get { return m_Position.Y; }
            private set
            {
                if (value.Equals(m_Position.Y)) return;
                m_Position.Y = value;
                UpdateBoundingBox();
                RaisePropertyChanged("PosY");
                RaisePropertyChanged("Position");
            }
        }

        public Point Position
        {
            get { return m_Position; }
            private set
            {
                if (value.Equals(m_Position)) return;
                m_Position = value;
                UpdateBoundingBox();
                RaisePropertyChanged("Position");
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("PosY");
            }
        }

        #endregion
    }
}
